// Author: dnorlov@google.com (Dmitry Orlov)

#ifndef ASSEMBLER_ASSEMBLER_PARSER_
#define ASSEMBLER_ASSEMBLER_PARSER_

#include "command.h"

namespace assembler {

struct Lexem {
  enum Type {
    NO_TYPE = 0,
    COMMAND_NAME,
    REGISTER_NAME,
    FUNCTION_NAME,
    CONSTANT,
    COMMA
  };

  Type type;

  const char *lexem_str;
  int length;
};

class Lexer {
public:
  Lexer(const char *stream, int length);

  // Consumes next lexem from the stream. Returns false 
  // if the whole stream is read.
  // Assings result->type to NO_TYPE if invalid lexem found.
  bool ConsumeLexem(Lexem *result);

  // Return true if the whole stream is completely scanned.
  bool done();

private:
  static bool IsCorrectNonPunctuationSymbol(char c);

  static bool IsPunctuation(char c);

  static bool IsIncorrectSymbol(char c);

  // [A-Z]+
  static bool LooksLikeCommandName(const char *str, int len);

  // F[0-9]
  static bool LooksLikeRegisterName(const char *str, int len);

  // @[a-zA-Z0-9]+
  static bool LooksLikeFunctionName(const char *str, int len);
  
  // -?[0-9]*.?[0-9]+
  static bool LooksLikeConstant(const char *str, int len);

  void SkipSpaces();

  const char *str_;
  const char *end_;
};

class Parser {
 public:
  Parser(Lexer *lexer);

  bool ConsumeCommand();

  const Command &cmd() const {
    return last_command_;
  }

  bool is_valid() const {
    return !invalid_state_;
  }

 private:

  static const char *CommandName[NUM_COMMAND_TYPE];

  // The following expect methods read and parse next lexem and 
  // set invalid_state_ to true, if lexem doesn't match the expectation.
  CommandType ExpectCommand();
  CommandArgument ExpectArgument();
  void ExpectComma();

  static CommandType CommandLexemToType(const Lexem &lexem);

  Lexer *lexer_;
  bool invalid_state_;
  Command last_command_;
};

}  // namespace assembler

#endif